package GP;

import java.util.ArrayList;
import java.util.Random;

import tree.Tree;

public class Selection {
	
	private Random random;
	private ArrayList<Tree> population;
	
	public Selection(Random random,ArrayList<Tree> population) {
		this.random = random;
		this.population = population;
	}
	
	//Sum of all the fitness of each chromosome in population.
	private double getSumOfAllChromosomes() {
		double sum=0;
		for (Tree tree: population){
			sum = sum + tree.getFitness();
		}
		return sum;
	}
	
	//ExpVal(i,t) by Sigma Scaling.
	private double getExpValSigmaScaling(double fitness, double avg,double sigma) {
		double answer = 1;
		if (sigma != 0){
			answer = 1 + ((fitness - avg)/(2*(sigma)));
		}
		return answer;
	}
	
	//calculate Standard deviation by the formula
	private double calcSigma(double avg) {
		double sigma=0;
		for (Tree tree:population){
			sigma+=Math.pow(tree.getFitness()-avg, 2);
		}
		sigma = sigma/population.size();
		return  Math.sqrt(sigma);
	}
	
	//selection - stochastic universal sampling - algorithm that shown in class
	ArrayList<Tree> selectionSUS() {
		ArrayList<Tree> selected = new ArrayList<Tree>(); 
		double randNum = random.nextFloat();
		double avg = (getSumOfAllChromosomes())/(population.size());
		double sum=0;
		double sigma = calcSigma(avg);
		for(int i=0;i<population.size();i++){
			sum+= getExpValSigmaScaling(population.get(i).getFitness(),avg,sigma);
			//sum = sum + (population.get(i).getFitness()/avg);
			while(sum>randNum){
				selected.add(population.get(i));
				randNum++;
			}
		}
		return selected;
	}
	
	public ArrayList<Tree> rouletteWheelSelection() {
		ArrayList<Tree> selected = new ArrayList<Tree>(); 
		double avg = getSumOfAllChromosomes()/population.size();
		int size = (int) getSumExpVal(avg);
		//int size = (int) getSumExpValOfSigmaScaling(avg);
		double sigma = calcSigma(avg);
		for (int i=0; i<population.size();i++){
			double sum=0;
			int randNum = random.nextInt(size);
			for (Tree c: population){
				sum = sum + (c.getFitness()/avg);
				sum+=getExpValSigmaScaling(c.getFitness(),avg,sigma);
				if (sum>=randNum){
					selected.add(c);
					break;
				}
			}
		}
		return selected;
	}
	
	private double getSumExpVal(double avg) {
		double answer=0;
		for (Tree c: population){
			answer = answer + (c.getFitness()/avg);
		}
		return answer;
	}	
	public void setPopulation(ArrayList<Tree> population) {
		this.population = population;
	}
}
